package com.henry.utils;

import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import javax.crypto.Cipher;
import org.apache.commons.lang3.tuple.Pair;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

/**
 * @author Liu Hanlin
 * @create 2025-01-28 0:32
 */
public class CryptoUtils {

    static {
        // 注册 BouncyCastle 支持 SM2
        Security.addProvider(new BouncyCastleProvider());
    }

    /**
     * 生成 RSA 密钥对，并返回 Base64 编码的公钥和私钥
     * @param keySize RSA 密钥大小，例如 2048
     * @return 返回包含公钥和私钥的数组，第一个元素是公钥，第二个元素是私钥
     */
    public static Pair<String, String> generateRSAKeyPair(int keySize) throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(keySize);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();

        // 获取公钥和私钥
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();

        // 将公钥和私钥转换为 Base64 编码字符串
        String publicKeyBase64 = Base64.getEncoder().encodeToString(publicKey.getEncoded());
        String privateKeyBase64 = Base64.getEncoder().encodeToString(privateKey.getEncoded());

        return Pair.of(publicKeyBase64, privateKeyBase64);
    }

    /**
     * 使用 RSA 私钥对数据进行加密
     * @param data 待加密的数据
     * @param privateKeyBase64 Base64 编码的 RSA 私钥
     * @return 返回加密后的数据（Base64 编码）
     */
    public static String encryptRSAPrivateKey(String data, String privateKeyBase64) throws Exception {
        byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyBase64);
        PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        byte[] encryptedData = cipher.doFinal(data.getBytes());
        return Base64.getEncoder().encodeToString(encryptedData);
    }

    /**
     * 使用 RSA 公钥对加密数据进行解密
     * @param encryptedData Base64 编码的加密数据
     * @param publicKeyBase64 Base64 编码的 RSA 公钥
     * @return 解密后的数据
     */
    public static String decryptRSAPublicKey(String encryptedData, String publicKeyBase64) throws Exception {
        byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyBase64);
        PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicKeyBytes));
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        byte[] decryptedData = cipher.doFinal(Base64.getDecoder().decode(encryptedData));
        return new String(decryptedData);
    }

    /**
     * 使用 RSA 私钥对数据进行签名
     * @param data 待签名的数据
     * @param privateKeyBase64 Base64 编码的 RSA 私钥
     * @return 签名（Base64 编码）
     */
    public static String signRSA(String data, String privateKeyBase64) throws Exception {
        byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyBase64);
        PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initSign(privateKey);
        signature.update(data.getBytes());
        byte[] signedData = signature.sign();
        return Base64.getEncoder().encodeToString(signedData);
    }

    /**
     * 使用 RSA 公钥验证签名
     * @param data 原始数据
     * @param signatureBase64 Base64 编码的签名
     * @param publicKeyBase64 Base64 编码的 RSA 公钥
     * @return 返回验证结果，true 为签名验证通过，false 为验证失败
     */
    public static boolean verifyRSA(String data, String signatureBase64, String publicKeyBase64) throws Exception {
        byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyBase64);
        PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicKeyBytes));
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initVerify(publicKey);
        signature.update(data.getBytes());
        byte[] signatureBytes = Base64.getDecoder().decode(signatureBase64);
        return signature.verify(signatureBytes);
    }
    /**
     * 使用 RSA 公钥对数据进行加密
     * @param data 待加密的数据
     * @param publicKeyBase64 Base64 编码的 RSA 公钥
     * @return 返回加密后的数据（Base64 编码）
     */
    public static String encryptRSAPublicKey(String data, String publicKeyBase64) throws Exception {
        byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyBase64);
        PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(publicKeyBytes));
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] encryptedData = cipher.doFinal(data.getBytes());
        return Base64.getEncoder().encodeToString(encryptedData);
    }

    /**
     * 使用 RSA 私钥对加密数据进行解密
     * @param encryptedData Base64 编码的加密数据
     * @param privateKeyBase64 Base64 编码的 RSA 私钥
     * @return 解密后的数据
     */
    public static String decryptRSAPrivateKey(String encryptedData, String privateKeyBase64) throws Exception {
        byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyBase64);
        PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] decryptedData = cipher.doFinal(Base64.getDecoder().decode(encryptedData));
        return new String(decryptedData);
    }


    /**
     * 生成 SM2 密钥对，并返回 Base64 编码的公钥和私钥
     * @return 返回包含公钥和私钥的数组，第一个元素是公钥，第二个元素是私钥
     */
    public static Pair<String ,String> generateSM2KeyPair() throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("SM2", "BC");
        KeyPair keyPair = keyPairGenerator.generateKeyPair();

        // 获取公钥和私钥
        ECPrivateKey privateKey = (ECPrivateKey) keyPair.getPrivate();
        ECPublicKey publicKey = (ECPublicKey) keyPair.getPublic();

        // 将公钥和私钥转换为 Base64 编码字符串
        String publicKeyBase64 = Base64.getEncoder().encodeToString(publicKey.getEncoded());
        String privateKeyBase64 = Base64.getEncoder().encodeToString(privateKey.getEncoded());

        return Pair.of(publicKeyBase64, privateKeyBase64);
    }

    /**
     * 使用 SM2 私钥对数据进行加密
     * @param data 待加密的数据
     * @param privateKeyBase64 Base64 编码的 SM2 私钥
     * @return 返回加密后的数据（Base64 编码）
     */
    public static String encryptSM2PrivateKey(String data, String privateKeyBase64) throws Exception {
        byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyBase64);
        PrivateKey privateKey = KeyFactory.getInstance("SM2", "BC").generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
        Cipher cipher = Cipher.getInstance("SM2", "BC");
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        byte[] encryptedData = cipher.doFinal(data.getBytes());
        return Base64.getEncoder().encodeToString(encryptedData);
    }

    /**
     * 使用 SM2 公钥对加密数据进行解密
     * @param encryptedData Base64 编码的加密数据
     * @param publicKeyBase64 Base64 编码的 SM2 公钥
     * @return 解密后的数据
     */
    public static String decryptSM2PublicKey(String encryptedData, String publicKeyBase64) throws Exception {
        byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyBase64);
        PublicKey publicKey = KeyFactory.getInstance("SM2", "BC").generatePublic(new X509EncodedKeySpec(publicKeyBytes));
        Cipher cipher = Cipher.getInstance("SM2", "BC");
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        byte[] decryptedData = cipher.doFinal(Base64.getDecoder().decode(encryptedData));
        return new String(decryptedData);
    }

    /**
     * 使用 SM2 私钥对数据进行签名
     * @param data 待签名的数据
     * @param privateKeyBase64 Base64 编码的 SM2 私钥
     * @return 签名（Base64 编码）
     */
    public static String signSM2(String data, String privateKeyBase64) throws Exception {
        byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyBase64);
        PrivateKey privateKey = KeyFactory.getInstance("SM2", "BC").generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
        Signature signature = Signature.getInstance("SM3withSM2", "BC");
        signature.initSign(privateKey);
        signature.update(data.getBytes());
        byte[] signedData = signature.sign();
        return Base64.getEncoder().encodeToString(signedData);
    }

    /**
     * 使用 SM2 公钥验证签名
     * @param data 原始数据
     * @param signatureBase64 Base64 编码的签名
     * @param publicKeyBase64 Base64 编码的 SM2 公钥
     * @return 返回验证结果，true 为签名验证通过，false 为验证失败
     */
    public static boolean verifySM2(String data, String signatureBase64, String publicKeyBase64) throws Exception {
        byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyBase64);
        PublicKey publicKey = KeyFactory.getInstance("SM2", "BC").generatePublic(new X509EncodedKeySpec(publicKeyBytes));
        Signature signature = Signature.getInstance("SM3withSM2", "BC");
        signature.initVerify(publicKey);
        signature.update(data.getBytes());
        byte[] signatureBytes = Base64.getDecoder().decode(signatureBase64);
        return signature.verify(signatureBytes);
    }


    /**
     * 使用 SM2 公钥对数据进行加密
     * @param data 待加密的数据
     * @param publicKeyBase64 Base64 编码的 SM2 公钥
     * @return 返回加密后的数据（Base64 编码）
     */
    public static String encryptSM2PublicKey(String data, String publicKeyBase64) throws Exception {
        byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyBase64);
        PublicKey publicKey = KeyFactory.getInstance("SM2", "BC").generatePublic(new X509EncodedKeySpec(publicKeyBytes));
        Cipher cipher = Cipher.getInstance("SM2", "BC");
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] encryptedData = cipher.doFinal(data.getBytes());
        return Base64.getEncoder().encodeToString(encryptedData);
    }

    /**
     * 使用 SM2 私钥对加密数据进行解密
     * @param encryptedData Base64 编码的加密数据
     * @param privateKeyBase64 Base64 编码的 SM2 私钥
     * @return 解密后的数据
     */
    public static String decryptSM2PrivateKey(String encryptedData, String privateKeyBase64) throws Exception {
        byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyBase64);
        PrivateKey privateKey = KeyFactory.getInstance("SM2", "BC").generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes));
        Cipher cipher = Cipher.getInstance("SM2", "BC");
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] decryptedData = cipher.doFinal(Base64.getDecoder().decode(encryptedData));
        return new String(decryptedData);
    }


    public static void main(String[] args) throws Exception {
        // Pair<String, String> generateRSAKeyPair = CryptoUtils.generateRSAKeyPair(1024);
        // String publicKeyBase64 = generateRSAKeyPair.getLeft();
        // String privateKeyBase64 = generateRSAKeyPair.getRight();
        // System.out.println("publicKeyBase64 = " + publicKeyBase64);
        // System.out.println("privateKeyBase64 = " + privateKeyBase64);
        //
        // String encryptedData = CryptoUtils.encryptRSAPublicKey ("Hello, World!", publicKeyBase64);
        //
        // System.out.println("明文 = Hello, World!");
        // System.out.println("密文 = " + encryptedData);
        // String decryptedData = CryptoUtils.decryptRSAPrivateKey (encryptedData, privateKeyBase64);
        // System.out.println("解密后 = " + decryptedData);
        //
        // String s = encryptAEC("SDF", "hhhhhhhhhhhhhhh1");
        // System.out.println(s);
        // System.out.println(decryptAEC(s, "hhhhhhhhhhhhhhh1"));

        // String aesKey = "OZ7OnxzgryvlYXIF91khgkfsAQj1ln5x";
        //
        // String encrypted = encryptAEC("123", aesKey);
        // System.out.println("加密后：" + encrypted);
        //
        // System.out.println("解密后：" + decryptAEC(encrypted, aesKey));


    }
}
